#include "uart.h"
#include "serial.h"
#include "stm32f4xx_conf.h"
#include "string.h"

/* Definition for USART1 resources ******************************************/
#define UART1_GPIO_TX       GPIO_Pin_9
#define UART1_TX_PIN_SOURCE GPIO_PinSource9
#define UART1_GPIO_RX       GPIO_Pin_10
#define UART1_RX_PIN_SOURCE GPIO_PinSource10
#define UART1_GPIO          GPIOA
#define UART1_GPIO_RCC      RCC_AHB1Periph_GPIOA
#define RCC_APBPeriph_UART1 RCC_APB2Periph_USART1
#define UART1_TX_DMA        DMA1_Channel4
#define UART1_RX_DMA        DMA1_Channel5

#define UART2_GPIO_TX       GPIO_Pin_2
#define UART2_TX_PIN_SOURCE GPIO_PinSource2
#define UART2_GPIO_RX       GPIO_Pin_3
#define UART2_RX_PIN_SOURCE GPIO_PinSource3
#define UART2_GPIO          GPIOA
#define UART2_GPIO_RCC      RCC_AHB1Periph_GPIOA
#define RCC_APBPeriph_UART2 RCC_APB1Periph_USART2
#define UART2_TX_DMA        DMA1_Channel4
#define UART2_RX_DMA        DMA1_Channel5

#define UART3_GPIO_TX       GPIO_Pin_8
#define UART3_TX_PIN_SOURCE GPIO_PinSource8
#define UART3_GPIO_RX       GPIO_Pin_9
#define UART3_RX_PIN_SOURCE GPIO_PinSource9
#define UART3_GPIO          GPIOD
#define UART3_GPIO_RCC      RCC_AHB1Periph_GPIOD
#define RCC_APBPeriph_UART3 RCC_APB1Periph_USART3
#define UART3_TX_DMA        DMA1_Stream1
#define UART3_RX_DMA        DMA1_Stream3



static int stm32_uart_configure(serial_device_t *serial, serial_config_t *config);

static int stm32_uart_control(serial_device_t *serial, int cmd, void *arg);
static int stm32_uart_putc(serial_device_t *serial, char c);

static int stm32_uart_getc(serial_device_t *serial);






typedef const struct _stm32_uart {
    USART_TypeDef *hw_entry;
    IRQn_Type irq;
} stm32_uart_t;

static stm32_uart_t uart_list[STM32F4_UART_MAX_NUM] = {
    {USART1, USART1_IRQn},
    {USART2, USART2_IRQn},
    {USART3, USART3_IRQn},
};
static serial_device_t serial_list[STM32F4_UART_MAX_NUM];

static serial_ops_t stm32_uart_ops = {
    stm32_uart_configure,
    stm32_uart_control,
    stm32_uart_putc,
    stm32_uart_getc,
};


static void stm32_uart_nvic_configuration(stm32_uart_t *uart)
{
    NVIC_InitTypeDef NVIC_InitStructure;

    /* Enable the USART1 Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = uart->irq;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}


static int stm32_uart_configure(serial_device_t *serial, serial_config_t *config)
{
    stm32_uart_t *uart;
    USART_InitTypeDef USART_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    assert(serial != NULL);
    assert(config != NULL);

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

    if (serial == &serial_list[0]) {
        RCC_AHB1PeriphClockCmd(UART1_GPIO_RCC, ENABLE);
        RCC_APB2PeriphClockCmd(RCC_APBPeriph_UART1, ENABLE);
        /* Configure USART1 Rx/tx PIN */
        GPIO_InitStructure.GPIO_Pin = UART1_GPIO_RX | UART1_GPIO_TX;
        GPIO_Init(UART1_GPIO, &GPIO_InitStructure);
        /* Connect alternate function */
        GPIO_PinAFConfig(UART1_GPIO, UART1_TX_PIN_SOURCE, GPIO_AF_USART1);
        GPIO_PinAFConfig(UART1_GPIO, UART1_RX_PIN_SOURCE, GPIO_AF_USART1);
    } else if (serial == &serial_list[1]) {
        RCC_AHB1PeriphClockCmd(UART2_GPIO_RCC, ENABLE);
        RCC_APB1PeriphClockCmd(RCC_APBPeriph_UART2, ENABLE);
        /* Configure USART2 Rx/tx PIN */
        GPIO_InitStructure.GPIO_Pin = UART2_GPIO_RX | UART2_GPIO_TX;
        GPIO_Init(UART2_GPIO, &GPIO_InitStructure);
        /* Connect alternate function */
        GPIO_PinAFConfig(UART2_GPIO, UART2_TX_PIN_SOURCE, GPIO_AF_USART2);
        GPIO_PinAFConfig(UART2_GPIO, UART2_RX_PIN_SOURCE, GPIO_AF_USART2);
    } else if (serial == &serial_list[2]) {
        RCC_AHB1PeriphClockCmd(UART3_GPIO_RCC, ENABLE);
        RCC_APB1PeriphClockCmd(RCC_APBPeriph_UART3, ENABLE);
        /* Configure USART3 Rx/tx PIN */
        GPIO_InitStructure.GPIO_Pin = UART3_GPIO_TX | UART3_GPIO_RX;
        GPIO_Init(UART3_GPIO, &GPIO_InitStructure);
        /* Connect alternate function */
        GPIO_PinAFConfig(UART3_GPIO, UART3_TX_PIN_SOURCE, GPIO_AF_USART3);
        GPIO_PinAFConfig(UART3_GPIO, UART3_RX_PIN_SOURCE, GPIO_AF_USART3);
    } else {
        assert(0);
    }

    uart = (stm32_uart_t *)serial->device.private;
    stm32_uart_nvic_configuration(uart);

    if (config->baud_rate == BAUD_RATE_9600)
        USART_InitStructure.USART_BaudRate = 9600;
    else if (config->baud_rate == BAUD_RATE_115200)
        USART_InitStructure.USART_BaudRate = 115200;

    if (config->data_bits == DATA_BITS_8)
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;

    if (config->stop_bits == STOP_BITS_1)
        USART_InitStructure.USART_StopBits = USART_StopBits_1;
    else if (config->stop_bits == STOP_BITS_2)
        USART_InitStructure.USART_StopBits = USART_StopBits_2;

    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(uart->hw_entry, &USART_InitStructure);

    /* Enable USART */
    USART_Cmd(uart->hw_entry, ENABLE);
    return 0;
}

static int stm32_uart_control(serial_device_t *serial, int cmd, void *arg)
{
    return 0;
}

static int stm32_uart_putc(serial_device_t *serial, char c)
{
    stm32_uart_t *uart;
    assert(serial != NULL);

    uart = (stm32_uart_t *)serial->device.private;

    while (!(uart->hw_entry->SR & USART_FLAG_TXE));
    uart->hw_entry->DR = c;

    return 1;
}

static int stm32_uart_getc(serial_device_t *serial)
{
    int ch = -1;
    stm32_uart_t *uart;
    assert(serial != NULL);

    uart = (stm32_uart_t *)serial->device.private;
#if 0
    if (uart->hw_entry->SR & USART_FLAG_RXNE)
        ch = uart->hw_entry->DR & 0xFF;
#else
    while (USART_GetFlagStatus(uart->hw_entry, USART_FLAG_RXNE) == RESET) {
    }
    ch = USART_ReceiveData(uart->hw_entry);
#endif
    return ch;
}

int stm32f4_hw_uart_init(void)
{
    const stm32_uart_t *uart;
    serial_device_t *serial;
    device_t *device;
    serial_config_t config = SERIAL_CONFIG_DEFAULT;
    char buff[100];

    memset(serial_list, 0x0, sizeof(serial_device_t) * STM32F4_UART_MAX_NUM);

    uart = &uart_list[0];
    serial = &serial_list[0];
    device = (device_t *)serial;
    serial->ops = &stm32_uart_ops;
    serial->config = config;

    /* Register to device tree(SW) */
    hw_serial_register(serial, "uart1", 0, (void *)uart);
    /* Initilize HW configuration. */
    device->init(device);
    device->write(device, 0, "\n", 2);

    return 0;
}


